#include "SATA_SSD.h"

SATA_SSD::SATA_SSD(sc_module_name	name_,
				   unsigned			SSD_DMA_PRD_start_addr_,
				   unsigned			Main_SSD_DMA_cmd_reg_start_addr_,
				   unsigned			Main_DMA_check_reg_start_addr_,
				   unsigned			System_type_,
				   unsigned			DRAM_rank_index_,
				   unsigned			DRAM_bank_index_,
				   unsigned			DRAM_row_index_,
				   unsigned			DRAM_col_index_)
	: sc_module(name_)
	, SSD_DMA_PRD_start_addr			(SSD_DMA_PRD_start_addr_)
	, SSD_DMA_PRD_start_addr_init		(SSD_DMA_PRD_start_addr_)
	, Main_SSD_DMA_cmd_reg_start_addr	(Main_SSD_DMA_cmd_reg_start_addr_)
	, Main_DMA_check_reg_start_addr		(Main_DMA_check_reg_start_addr_)
	, System_type						(System_type_)
	, DRAM_rank_index					(DRAM_rank_index_)
	, DRAM_bank_index					(DRAM_bank_index_)
	, DRAM_row_index					(DRAM_row_index_)
	, DRAM_col_index					(DRAM_col_index_)
	, SSD_RD_pIN						(0)
	, SSD_RD_pOUT						(0)
	, SSD_WR_pIN						(0)
	, SSD_WR_pOUT						(0)
	, DMA_SSD_RD_Buffer_pOUT			(0)
	, DMA_SSD_RD_Buffer_pIN				(0)
	, DRAM_RD_pIN						(0)
	, DRAM_RD_pOUT						(0)
	, DRAM_WR_pIN						(0)
	, DRAM_WR_pOUT						(0)
	, DMA_DRAM_RD_Buffer_pOUT			(0)
	, DMA_DRAM_RD_Buffer_pIN			(0)
	, CH1_Buffer_pIN					(0)
	, CH1_Buffer_pOUT					(0)
	, CH2_Buffer_pIN					(0)
	, CH2_Buffer_pOUT					(0)
	, DRAM_refresh_enable				(false)
	, SSD_DMA_enable					(false)
	, SSD_DMA_PRD_fetched				(false)
	, SSD_DMA_buffered					(false)
	, SSD_DMA_end						(0x0)
	, current_state_1					(T_IDLE)
	, current_state_2					(T_IDLE)
	, LBA_write							(0x0)
	, LBA_read							(0x0)
	, C_WRITE							(false)
	, C_READ							(false)
	, DRAM_or_SSD						(true)
	, SSD_Cache_WR_request				(false)
	, SSD_Cache_RD_request				(false)
	, DRAM_Cache_WR_request				(false)
	, DRAM_Cache_RD_request				(false)
	, SSD_Cache_finished				(true)
	, DRAM_Cache_finished				(true)
	, SSD_DMA_commanded					(false)
	, F_WRITE_1							(false)
	, F_READ_1							(false)
	, F_ERASE_1							(false)
	, F_WRITE_2							(false)
	, F_READ_2							(false)
	, F_ERASE_2							(false)
	, DATA_cnt_1						(0)
	, DATA_cnt_2						(0)
	, DATA_word_1						(0)
	, DATA_word_2						(0)
	, Confirm_commanded_1				(false)
	, Confirm_commanded_2				(false)
	, cache_buffer_read_1				(false)
	, cache_buffer_read_2				(false)
{
	transport_currentstate = DT_DeviceIdle;
	command_currentstate = Cmd_Device_idle;
		
	SC_METHOD(Transport_FSM_process);
	sensitive <<  SATA_CLK.pos();
	dont_initialize();

	SC_METHOD(Command_FSM_process);
	sensitive << SATA_CLK.pos();
	dont_initialize();

	SC_METHOD(FIFO_process);
	sensitive<< SATA_CLK.pos();
	dont_initialize();

	SC_THREAD(Cache_process);
	sensitive<< SATA_CLK.pos();
	dont_initialize();

	SC_THREAD(FLASHcontroller_process_1);
	sensitive << FLASH_CLK.pos();
	dont_initialize();

	SC_THREAD(FLASHcontroller_process_2);
	sensitive << FLASH_CLK.pos();
	dont_initialize();

	SC_THREAD(DRAM_timing_control_action);
	dont_initialize();
	sensitive << DRAM_CLK;

	SC_THREAD(Cache_arbiter_action);
	dont_initialize();
	sensitive << SATA_CLK.pos();

	HWRITE = HREAD = false;
		
	RegDHFIS_req = DMAACTFIS_req = DMASTUPFIS_req = false;
	DataFIS_req = false;
	crc_buffer_empty = false;

	FIS_receipt = 0;
	frame_receipt = 0;
	flush_RD_FIFO = 0;
	STATUS_reg = 1;

	for(int i=0; i<CACHE_BUFFER; i++)
		SRAM_Array[i] = i;
}

SATA_SSD::~SATA_SSD()
{
}

/***********************************************************************/
/********************* Device Transport layer **************************/
/***********************************************************************/

void SATA_SSD::Transport_FSM_process()
{
	
	switch(transport_currentstate)
	{
		// Device to Host FIS
		case DT_DeviceIdle:
#ifdef	DEBUG_SATA_SSD
//			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> DT_DeviceIdle"<<endl;
#endif
			frame_receipt = HTx_Dword.read();
			{
				if(RegDHFIS_req)
				{
#ifdef	DEBUG_SATA_SSD
					cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Send SOF...go to DT_RegDHFIS0"<<endl;
#endif
					HRx_Dword.write(SOF);
					transport_currentstate = DT_RegDHFIS0;
				}
				else if(DMAACTFIS_req)
				{
#ifdef	DEBUG_SATA_SSD
					cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Send SOF...go to DT_DMAACTFIS"<<endl;
#endif
					HRx_Dword.write(SOF);
					transport_currentstate = DT_DMAACTFIS;
				}
				else if(DMASTUPFIS_req)
				{					
					transport_currentstate = DT_DMASTUPDHFIS;
				}
				else if(DataFIS_req)
				{
#ifdef	DEBUG_SATA_SSD
					cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Send SOF...go to DT_DataIFIS" << endl;
#endif
					HRx_Dword.write(SOF);
					transport_currentstate = DT_DataIFIS;
				}
				else if(frame_receipt == SOF)
				{
#ifdef	DEBUG_SATA_SSD
					cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Receive SOF...go to DT_ChkTyp" << endl;
#endif
					transport_currentstate = DT_ChkTyp;
				}
			}
			break;
//////////// Device to Host FIS /////////////////////////////////////
		// Register - Device to Host
		case DT_RegDHFIS0:
			RegDHFIS_req = false;
			Reg_DH0 = (Status<<16) |(ssd_interrupt<<14) |(RegD2H_FIS);
#ifdef	DEBUG_SATA_SSD
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> DT_RegDHFIS0 : " << Reg_DH0 << endl;
#endif
			HRx_Dword.write(Reg_DH0);
			transport_currentstate = DT_RegDHFIS1;
			break;
		case DT_RegDHFIS1:
			Reg_DH1 = (Dev_Head<<24);				
#ifdef	DEBUG_SATA_SSD
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> DT_RegDHFIS1 : " << Reg_DH1 << endl;
#endif
			HRx_Dword.write(Reg_DH1);
			transport_currentstate = DT_RegDHFIS2;
			break;
		case DT_RegDHFIS2:
			Reg_DH2 = 0;
#ifdef	DEBUG_SATA_SSD
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> DT_RegDHFIS2 : " << Reg_DH2 << endl;
#endif
			HRx_Dword.write(Reg_DH2);
			transport_currentstate = DT_RegDHFIS3;
			break;
		case DT_RegDHFIS3:
			Reg_DH3 = 0;
#ifdef	DEBUG_SATA_SSD
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> DT_RegDHFIS3 : " << Reg_DH3 << endl;
#endif
			HRx_Dword.write(Reg_DH3);
			transport_currentstate = DT_RegDHFIS4;
			break;
		case DT_RegDHFIS4:
			Reg_DH4 = 0;
#ifdef	DEBUG_SATA_SSD
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> DT_RegDHFIS4 : " << Reg_DH4 << endl;
#endif
			HRx_Dword.write(Reg_DH4);
			crc_buffer_cnt = CRC_BUFFER+1;
			transport_currentstate = DT_DeviceIdle;
			break;
		// DMA Activate - Device to Host
		case DT_DMAACTFIS:
#ifdef	DEBUG_SATA_SSD
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> DT_DMAACTFIS"<<endl;
#endif
			{
				HRx_Dword.write(DMAact_FIS);
				transport_currentstate = DT_DeviceIdle;
			}
			break;

		case DT_DataIFIS:
#ifdef	DEBUG_SATA_SSD
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> DT_DataIFIS"<<endl;
#endif
			{
				flush_RD_FIFO = 1;
				if (LBA_read_temp >= Main_SSD_DMA_cmd_reg_start_addr>>12)
					crc_buffer_cnt = 1;
				else
					crc_buffer_cnt = CRC_BUFFER+1;
				transport_currentstate = DT_DATAItran;
				HRx_Dword.write(Data_FIS);
			}
			break;

		// DATA - Device to Host
		case DT_DATAItran:
#ifdef	DEBUG_SATA_SSD
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> DT_DATAItran "<<endl;
#endif
			{
				if ((crc_buffer_cnt == 1) && (LBA >= Main_DMA_check_reg_start_addr>>12))
				{
					HRx_Dword.write(SSD_DMA_end);
#ifdef	DEBUG_SATA_SSD
					cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Transmit READ DATA: " << hex << SSD_DMA_end << dec << endl;
#endif
				}
				else
				{
					HRx_Dword.write(RD_FIFO_temp);
#ifdef	DEBUG_SATA_SSD
					cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Transmit READ DATA: " << hex <<RD_FIFO_temp << dec << endl;
#endif
				}
#ifdef	DEBUG_SATA_SSD
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Transfered count(buffer size): " << crc_buffer_cnt << endl;
#endif
				crc_buffer_cnt--;
				if(crc_buffer_cnt == 0)
				{
					iteration--;
					flush_RD_FIFO = 0;
					transport_currentstate = DT_DeviceIdle;
				}
			}			
			break;
///////////// Host to Device FIS ////////////////////////////////////
		case DT_ChkTyp:
#ifdef	DEBUG_SATA_SSD
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> DT_ChkTyp: "<<hex<<HTx_Dword.read()<< dec << endl;
#endif
			{
				frame_receipt = 0;
				FIS_type = HTx_Dword.read() & 0xFF;
								
				if(FIS_type == RegH2D_FIS) {
					C = (HTx_Dword.read()>>15) & 0x1;
					Command = (HTx_Dword.read()>>16) & 0xFF;
					transport_currentstate = DT_RegHDFIS1;
				}
				else if(FIS_type == Data_FIS)
					transport_currentstate = DT_DataOFIS;
				else if(FIS_type == DMAsetup_FIS)	
					transport_currentstate = DT_DMASTUPDHFIS;
				else
					transport_currentstate = DT_DeviceIdle;
			}
			break;
/*
		case DT_RegHDFIS0:
#ifdef	DEBUG_SATA_SSD
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> DT_RegHDFIS0: " <<HTx_Dword.read()<< dec << endl;
#endif
			{
				//RegH2DFIS0 = Dword.read();
				C = (HTx_Dword.read()>>15) & 0x1;
				Command = (HTx_Dword.read()>>16) & 0xFF;
				transport_currentstate = DT_RegHDFIS1;
			}
			break;
*/
		case DT_RegHDFIS1:
#ifdef	DEBUG_SATA_SSD
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> DT_RegHDFIS1: "<<hex<<HTx_Dword.read()<< dec << endl;
#endif
			LBA = HTx_Dword.read();
			transport_currentstate = DT_RegHDFIS2;
			break;
		case DT_RegHDFIS2:
#ifdef	DEBUG_SATA_SSD
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> DT_RegHDFIS2: "<<hex<<HTx_Dword.read()<< dec << endl;
#endif
			LBA_Extend = HTx_Dword.read();
			transport_currentstate = DT_RegHDFIS3;
			break;
		case DT_RegHDFIS3:
#ifdef	DEBUG_SATA_SSD
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> DT_RegHDFIS3: "<<hex<<HTx_Dword.read()<< dec << endl;
#endif
			Sector_cnt = HTx_Dword.read() & 0xFF;
			transfer_cnt = (Sector_cnt*(512/4));
			iteration = transfer_cnt/CRC_BUFFER;
			Control = (HTx_Dword.read()>>24) & 0xFF;
			transport_currentstate = DT_RegHDFIS4;
			break;
		case DT_RegHDFIS4:
#ifdef	DEBUG_SATA_SSD
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> DT_RegHDFIS4"<<endl;
#endif
			transport_currentstate = DT_DeviceIdle;
			FIS_receipt = true;
			break;
		case DT_DataOFIS: 
#ifdef	DEBUG_SATA_SSD
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> DT_DataOFIS"<<endl;
#endif
			{
				if (HTx_Dword.read() == 0)
				{
					SSD_DMA_commanded = true;
					SSD_DMA_end = 0x0;
					crc_buffer_cnt = 4;
				}
				else
				{
					crc_buffer_cnt = CRC_BUFFER+1;
				}
				transport_currentstate = DT_DATAOREC;
			}
			break;
		
		case DT_DATAOREC: // insert to the FIFO
#ifdef	DEBUG_SATA_SSD
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> DT_DATAOREC  Receive DATA: 0x"<< hex << HTx_Dword.read() << dec <<endl;
#endif
			{
#ifdef	DEBUG_SATA_SSD
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> DT_DATAOREC  Received WRITE DATA: 0x"<< hex << HTx_Dword.read() << dec << endl;
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Transfered count(buffer size): " << crc_buffer_cnt << endl;
#endif
					WR_FIFO_temp = HTx_Dword.read();
					if (SSD_DMA_commanded)
					{
						if (crc_buffer_cnt == 4)
							SSD_DMA_command = WR_FIFO_temp;
						else if (crc_buffer_cnt == 3)
							SSD_DMA_sector_cnt = WR_FIFO_temp;
						else if (crc_buffer_cnt == 2)
							SSD_DMA_LBA = SSD_DMA_LBA_current = WR_FIFO_temp;
						else if (crc_buffer_cnt == 1)
							SSD_DMA_PRD_length = WR_FIFO_temp;
					}
					else
					{
						HWRITE = true;
					}
					crc_buffer_cnt--;
					if(crc_buffer_cnt == 0)
					{
						if (SSD_DMA_commanded)
						{
//#ifdef	DEBUG_SATA_SSD
							cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Write to SATA is finished...South SSD-DMA enabled." <<endl;
//#endif
							if (SSD_DMA_command == 0xC8)
								DRAM_Cache_RD_request = true;
							PRD_current = SSD_DMA_sector_cnt/(4096/512);
							DMA_SSD_RD_Buffer_pIN = DMA_SSD_RD_Buffer_pOUT = 0;
							DMA_DRAM_RD_Buffer_pIN = DMA_DRAM_RD_Buffer_pOUT = 0;
							SSD_WR_pIN = 0;

							SSD_DMA_enable = true;
							SSD_DMA_enable_time = sc_time_stamp();
							SSD_DMA_commanded = false;

							crc_buffer_cnt = CRC_BUFFER+1;
						}
						else
						{
							iteration--;
							HWRITE = false;
						}
						transport_currentstate = DT_DeviceIdle;
					}
					else
						transport_currentstate = DT_DATAOREC;
				//}
			
			/*
			if((!crc_buffer_full) && (transfer_cnt != 0))
				{
#ifdef	DEBUG_SATA_SSD
					cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> DT_DATAOREC  Received WRITE DATA: "<<HTx_Dword.read()<<endl;
					cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Total transfered count: " << transfer_cnt << endl;
#endif
					WR_FIFO_temp = HTx_Dword.read();
					HWRITE = 1;
					transfer_cnt--;
					transport_currentstate = DT_DATAOREC;
				}
				else if(crc_buffer_full) {
					HWRITE = 0;
					transport_currentstate = DT_DeviceIdle;
				}				
				*/
			}
			break;

		case DT_DMASTUPDHFIS:
			{
				transport_currentstate = DT_DeviceIdle;
			}
			break;
	}
}

/****************************************************************/
/******************** Command layer *****************************/
/****************************************************************/

void SATA_SSD::Command_FSM_process()
{
	switch(command_currentstate)
	{
		case Cmd_Device_idle:
			RegDHFIS_req = false;
			DataFIS_req = false;

			if(FIS_receipt)
			{
				FIS_receipt = 0;
				command_currentstate = Check_FIS;
			}
			break;
		case Check_FIS:
#ifdef	DEBUG_SATA_SSD
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Cmd_check_FIS"<<endl;
#endif
			if((FIS_type == RegH2D_FIS) && C)
				command_currentstate = Check_command;
			else
				command_currentstate = Cmd_Device_idle;
			break;
		case Check_command:
#ifdef	DEBUG_SATA_SSD
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Check_command"<<endl;
#endif
			if(Command == READ_DMA)
			{
				LBA_read_temp = LBA;
				if (LBA_read_temp < Main_DMA_check_reg_start_addr>>12)
					SSD_Cache_RD_request = true;
				command_currentstate = DMA_in;
			}
			else if(Command == WRITE_DMA)
			{
				LBA_write_temp = LBA;
				command_currentstate = DMA_out;
			}
			else
			{
				command_currentstate = Cmd_Device_idle;
			}
			break;
		case DMA_in:
#ifdef	DEBUG_SATA_SSD
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> DMA_in"<<endl;
#endif
			{
				if (LBA_read_temp < Main_DMA_check_reg_start_addr>>12)
				{
					if(HREAD)
					{
						DataFIS_req = true;
						command_currentstate = Send_data;
					}
				}
				else
				{
					DataFIS_req = true;
					command_currentstate = Send_data;
				}
			}
			break;
		case Send_data:
#ifdef	DEBUG_SATA_SSD
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Send_data"<<endl;
#endif
			{
				DataFIS_req = false;
				if (iteration == 0)
				{
					//STATUS_reg = 1;
					command_currentstate = RD_send_status;
				}
				else if(iteration && (crc_buffer_cnt == 0))
				{
					//STATUS_reg = 1;
					LBA_read_temp = LBA_read_temp + (4*CRC_BUFFER/SECTOR_SIZE);
					crc_buffer_cnt = CRC_BUFFER+1;
					command_currentstate = Check_command;
				}
			}
			break;
		case RD_send_status:
#ifdef	DEBUG_SATA_SSD
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> RD_send_status"<<endl;
#endif
			{
				RegDHFIS_req = true;
				Status = 0x00;
				ssd_interrupt = true;
				Dev_Head = 0x00;
				command_currentstate = Cmd_Device_idle;
			}
			break;
			
		//WRITE DMA
		case DMA_out:
#ifdef	DEBUG_SATA_SSD
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> DMA_out"<<endl;
#endif
			command_currentstate = Send_DMA_activate;
			break;

		case Send_DMA_activate:
#ifdef	DEBUG_SATA_SSD
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Send_DMA_activate"<<endl;
#endif
			{
				DMAACTFIS_req = true;
				command_currentstate = Receive_data;
			}
			break;

		case Receive_data:
#ifdef	DEBUG_SATA_SSD
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Receive_data"<<endl;
#endif
			DMAACTFIS_req = 0;
			if((iteration == 0) && !(STATUS_reg & 0x1))
			{
				frame_receipt = false;
				RegDHFIS_req = true;
				STATUS_reg = true;
				command_currentstate = WR_send_status;
			}
			else if(iteration && (crc_buffer_cnt == 0)&& !(STATUS_reg & 0x1))
			{
				STATUS_reg = 1;
				LBA_write_temp = LBA_write_temp + (4*CRC_BUFFER/SECTOR_SIZE);
				crc_buffer_cnt = CRC_BUFFER+1;
				command_currentstate = Send_DMA_activate;
			}
			else 
				command_currentstate = Receive_data;
			break;
		case WR_send_status:
#ifdef	DEBUG_SATA_SSD
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> WR_send_status"<<endl;
#endif
			Status = 0x00;
			Dev_Head = 0x00;
			command_currentstate = Cmd_Device_idle;
			break;
	}
}
	
//////////////////////buffer controller/////////////////////////
void SATA_SSD::FIFO_process()
{
		if(HWRITE)
		{
#ifdef	DEBUG_SATA_SSD
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> WRITE FIFO_process" << endl;
#endif
			SSD_WR_FIFO[SSD_WR_pIN] = WR_FIFO_temp;
#ifdef	DEBUG_SATA_SSD
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> SSD_WR_FIFO["<<SSD_WR_pIN<<"]: "<<SSD_WR_FIFO[SSD_WR_pIN]<<endl;
#endif
			SSD_WR_pIN= (++SSD_WR_pIN)%CRC_BUFFER;
			if(SSD_WR_pIN == 0)
			{
				SSD_Cache_WR_request = true;
//#ifdef	DEBUG_SATA_SSD
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Write to FIFO is finished...Enable Write to CACHE." <<endl;
//#endif
				//crc_buffer_full = true;
			}					
		} 
		else if(HREAD && flush_RD_FIFO) {
#ifdef	DEBUG_SATA_SSD
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> READ FIFO_process  "<<endl;
#endif
			//HRx_Dword.write(SSD_RD_FIFO[SSD_RD_pOUT]);
			RD_FIFO_temp = SSD_RD_FIFO[SSD_RD_pOUT];
			//cout <<"SSD_RD_FIFO["<<SSD_RD_pOUT<<"]: "<<RD_FIFO_temp<<endl;
			SSD_RD_pOUT= (++SSD_RD_pOUT)%CRC_BUFFER;
			if(SSD_RD_pOUT == 0) {
				HREAD = false;
//				crc_buffer_empty = true;
			}
		}
}

void
SATA_SSD::Cache_arbiter_action()
{
	while (true)
	{
		if ((SSD_Cache_WR_request || DRAM_Cache_WR_request) && (SSD_Cache_finished) && (DRAM_Cache_finished))
		{
#ifdef	DEBUG_SATA_SSD
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Start Write to CACHE." <<endl;
#endif
			C_WRITE = true;
			if (DRAM_Cache_WR_request)
			{
				DRAM_or_SSD = true;
				LBA_write = SSD_DMA_LBA_current;
				DRAM_Cache_WR_request = false;
				DRAM_Cache_finished = false;
			}
			else
			{
				DRAM_or_SSD = false;
				LBA_write = LBA_write_temp;
				SSD_Cache_WR_request = false;
				SSD_Cache_finished = false;
			}
		}
		else if ((SSD_Cache_RD_request || DRAM_Cache_RD_request) && (SSD_Cache_finished) && (DRAM_Cache_finished))
		{
//#ifdef	DEBUG_SATA_SSD
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> SSD Internal Read started." <<endl;
//#endif
			C_READ = true;
			if (DRAM_Cache_RD_request)
			{
				DRAM_or_SSD = true;
				LBA_read = SSD_DMA_LBA_current;
				DRAM_Cache_RD_request = false;
				DRAM_Cache_finished = false;
			}
			else
			{
				DRAM_or_SSD = false;
				LBA_read = LBA_read_temp;
				SSD_Cache_RD_request = false;
				SSD_Cache_finished = false;
			}
		}
		wait();
	}
}
	
const bool
SATA_SSD::Cache_decision(unsigned	LBA_) const
{
	if (decision[LBA_%(CACHE_BUFFER/CRC_BUFFER)] == LBA_)
		return true;
	else
		return false;
}

void
SATA_SSD::Cache_process()
{
	while (true)
	{
		if (C_WRITE)
		{
			if (((!DRAM_or_SSD) && (SSD_WR_pOUT < CRC_BUFFER)) || ((DRAM_or_SSD) && (DRAM_WR_pOUT < CRC_BUFFER)))
			{
				if (!DRAM_or_SSD)
				{
#ifdef	DEBUG_SATA_SSD
					cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Write to CACHE(" << SSD_WR_pOUT << ") : 0x" << hex << SSD_WR_FIFO[SSD_WR_pOUT] << dec << endl;
#endif
					SRAM_Array[(LBA_write%(CACHE_BUFFER/CRC_BUFFER))*CRC_BUFFER+SSD_WR_pOUT] = SSD_WR_FIFO[SSD_WR_pOUT];
					SSD_WR_pOUT++;
				}
				else
				{
#ifdef	DEBUG_SATA_SSD
					cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Write to CACHE(" << DRAM_WR_pOUT << ") : 0x" << hex << DRAM_WR_FIFO[DRAM_WR_pOUT] << dec << endl;
#endif
					SRAM_Array[(LBA_write%(CACHE_BUFFER/CRC_BUFFER))*CRC_BUFFER+DRAM_WR_pOUT] = DRAM_WR_FIFO[DRAM_WR_pOUT];
					DRAM_WR_pOUT++;
				}
			}
			else
			{
				if (LBA_write < (NAND_CH2_START_LBA<<12))
				{
					if (CH1_Buffer_pIN < CRC_BUFFER)
					{
						CH1_Buffer[CH1_Buffer_pIN] = SRAM_Array[(LBA_write%(CACHE_BUFFER/CRC_BUFFER))*CRC_BUFFER+CH1_Buffer_pIN];
#ifdef	DEBUG_SATA_SSD
						cout<<"["<<sc_time_stamp()<<"] CACHE_process> Write to CH1_Buffer : "<< CH1_Buffer_pIN << endl;
#endif
						CH1_Buffer_pIN++;
					}
					else if (current_state_1 == T_IDLE)
					{
#ifdef	DEBUG_SATA_SSD
						cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Write to CACHE & CH1_BUFFER is finished." << endl;
#endif
						decision[LBA_write%(CACHE_BUFFER/CRC_BUFFER)] = LBA_write;
						C_WRITE = false;
						if (!DRAM_or_SSD)
							SSD_WR_pOUT = 0;
						else
							DRAM_WR_pOUT = 0;
						F_WRITE_1 = true;
#ifdef	DEBUG_SATA_SSD
						cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Write to Flash CH1 is being requested." <<endl;
#endif
						CH1_Buffer_pIN = 0;
					}
				}
				else
				{
					if (CH2_Buffer_pIN < CRC_BUFFER)
					{
						CH2_Buffer[CH2_Buffer_pIN] = SRAM_Array[(LBA_write%(CACHE_BUFFER/CRC_BUFFER))*CRC_BUFFER+CH2_Buffer_pIN];
#ifdef	DEBUG_SATA_SSD
						cout<<"["<<sc_time_stamp()<<"] CACHE_process> Write to CH2_Buffer : "<< CH2_Buffer_pIN << endl;
#endif
						CH2_Buffer_pIN++;
					}
					else if (current_state_2 == T_IDLE)
					{
#ifdef	DEBUG_SATA_SSD
						cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Write to CACHE & CH2_BUFFER is finished." << endl;
#endif
						decision[LBA_write%(CACHE_BUFFER/CRC_BUFFER)] = LBA_write;
						C_WRITE = false;
						if (!DRAM_or_SSD)
							SSD_WR_pOUT = 0;
						else
							DRAM_WR_pOUT = 0;
						F_WRITE_2 = true;
#ifdef	DEBUG_SATA_SSD
						cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Write to Flash CH2 is being requested." <<endl;
#endif
						CH2_Buffer_pIN = 0;
					}
				}
			}
		}
		if (C_READ)
		{
//			if (Cache_decision(LBA_read))
			if (true)
			{
				if (!DRAM_or_SSD) 
				{
					if (SSD_RD_pIN < CRC_BUFFER)
					{
						if (SSD_RD_pIN == 0)
						{
//#ifdef	DEBUG_SATA_SSD
							cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> SSD I/F Read is started." <<endl;
//#endif
							HREAD = true;
						}
						SSD_RD_FIFO[SSD_RD_pIN] = SRAM_Array[(LBA_read%(CACHE_BUFFER/CRC_BUFFER))*CRC_BUFFER+SSD_RD_pIN];
#ifdef	DEBUG_SATA_SSD
						cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Read from CACHE(" << SSD_RD_pIN << ") : 0x" << hex << SSD_RD_FIFO[SSD_RD_pIN] << dec << endl;
#endif
						SSD_RD_pIN++;
					}
					else
					{
						C_READ = false;
						SSD_Cache_finished = true;
						SSD_RD_pIN = 0;
					}
				}
				else
				{
					if (DRAM_RD_pIN < CRC_BUFFER)
					{
						DRAM_RD_FIFO[DRAM_RD_pIN] = SRAM_Array[(LBA_read%(CACHE_BUFFER/CRC_BUFFER))*CRC_BUFFER+DRAM_RD_pIN];
#ifdef	DEBUG_SATA_SSD
						cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Read from CACHE(" << DRAM_RD_pIN << ") : 0x" << hex << DRAM_RD_FIFO[DRAM_RD_pIN] << dec << endl;
#endif
						DRAM_RD_pIN++;
					}
					else
					{
						C_READ = false;
						SSD_DMA_buffered = true;
						DRAM_Cache_finished = true;
						DRAM_RD_pIN = 0;
					}
				}
			}
			else
			{
				if (LBA_read < NAND_CH2_START_LBA<<12)
				{
					if (cache_buffer_read_1)
					{
						if (CH1_Buffer_pOUT < CRC_BUFFER)
						{
							SRAM_Array[(LBA_read%(CACHE_BUFFER/CRC_BUFFER))*CRC_BUFFER+CH1_Buffer_pOUT] = CH1_Buffer[CH1_Buffer_pOUT];
#ifdef	DEBUG_SATA_SSD
							cout<<"["<<sc_time_stamp()<<"] CACHE_process> READ from CH1_Buffer : "<< CH1_Buffer_pOUT << endl;
#endif
							CH1_Buffer_pOUT++;
						}
						else
						{
							decision[LBA_read%(CACHE_BUFFER/CRC_BUFFER)] = LBA_read;
							CH1_Buffer_pOUT = 0;
							cache_buffer_read_1 = false;
//#ifdef	DEBUG_SATA
							cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> SSD Internal Read finished"<<endl;
//#endif
						}
					}
					else
					{
						if ((!F_WRITE_1) && (current_state_1 == T_IDLE))
						{
							F_READ_1 = true;
#ifdef	DEBUG_SATA_SSD
							cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Read from Flash CH1 is being requested." <<endl;
#endif
						}
					}
				}
				else
				{
					if (cache_buffer_read_2)
					{
						if (CH2_Buffer_pOUT < CRC_BUFFER)
						{
							SRAM_Array[(LBA_read%(CACHE_BUFFER/CRC_BUFFER))*CRC_BUFFER+CH2_Buffer_pOUT] = CH2_Buffer[CH2_Buffer_pOUT];
#ifdef	DEBUG_SATA_SSD
							cout<<"["<<sc_time_stamp()<<"] CACHE_process> READ from CH2_Buffer : "<< CH2_Buffer_pOUT << endl;
#endif
							CH2_Buffer_pOUT++;
						}
						else
						{
							decision[LBA_read%(CACHE_BUFFER/CRC_BUFFER)] = LBA_read;
							CH2_Buffer_pOUT = 0;
							cache_buffer_read_2 = false;
//#ifdef	DEBUG_SATA_SSD
							cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> SSD Internal Read finished"<<endl;
//#endif
						}
					}
					else
					{
						if ((!F_WRITE_2) && (current_state_2 == T_IDLE))
						{
							F_READ_2 = true;
#ifdef	DEBUG_SATA_SSD
							cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Read from Flash CH2 is being requested." <<endl;
#endif
						}
					}
				}
			}
		}
		wait();
	}
}

void
SATA_SSD::FLASHcontroller_process_1()
{
	while (true)
	{
		switch(current_state_1)
		{
		case T_IDLE:
			if (F_WRITE_1 || F_READ_1 || F_ERASE_1)
			{
#ifdef	DEBUG_SATA_SSD
				cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_1> T_IDLE"<<endl;
#endif
				current_state_1 = T_COMMAND;
			}
			break;
		case T_COMMAND:
#ifdef	DEBUG_SATA_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_1> T_COMMAND"<<endl;
#endif
			if (F_WRITE_1)
			{ 
				FLASH_addr_gen(LBA_write);
				cmd_1 = NAND_START_PROGRAM;
				F_WRITE_1 = false;
				current_state_1 = T_COL_ADDR0;
			}
			else if (F_READ_1)
			{
				FLASH_addr_gen(LBA_read);
				cmd_1 = NAND_START_READ;
				F_READ_1 = false;
				current_state_1 = T_COL_ADDR0;
			}
			else if (F_ERASE_1)
			{
				cmd_1 = NAND_START_ERASE;
				F_ERASE_1 = false;
				current_state_1 = T_ROW_ADDR0;
			}
			else
			{
				cout << "[" << sc_time_stamp() << "] FLASH_CTRL_1> Unsupported command!" << endl;
				sc_stop();
			}
			NAND_port_1->NAND_write(cmd_1);
			break;
		case T_COL_ADDR0:
#ifdef	DEBUG_SATA_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_1> T_COL_ADDR0"<<endl;
#endif
			current_state_1 = T_COL_ADDR1;
			NAND_port_1->NAND_write(col_addr0);
			break;
		case T_COL_ADDR1:
#ifdef	DEBUG_SATA_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_1> T_COL_ADDR1"<<endl;
#endif
			current_state_1 = T_ROW_ADDR0;
			NAND_port_1->NAND_write(col_addr1);
			break;
		case T_ROW_ADDR0:
#ifdef	DEBUG_SATA_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_1> T_ROW_ADDR0"<<endl;
#endif
			current_state_1 = T_ROW_ADDR1;
			NAND_port_1->NAND_write(row_addr0);
			break;
		case T_ROW_ADDR1:
#ifdef	DEBUG_SATA_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_1> T_ROW_ADDR1"<<endl;
#endif
			NAND_port_1->NAND_write(row_addr1);
			if (cmd_1 == NAND_START_PROGRAM)
				current_state_1 = T_WR_DATA;
			else if (cmd_1 == NAND_START_READ)
				current_state_1 = T_CONFIRM;
			else if (cmd_1 == NAND_START_ERASE)
				current_state_1 = T_CONFIRM;
			break;
		case T_WR_DATA:
#ifdef	DEBUG_SATA_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_1> T_WR_DATA"<<endl;
#endif
			NAND_port_1->NAND_write((byte)(CH1_Buffer[DATA_cnt_1/4] >> ((DATA_cnt_1%4)*8)));
#ifdef	DEBUG_SATA_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_1> Write to NAND : 0x" << hex << (unsigned)((byte)(CH1_Buffer[DATA_cnt_1/4] >> ((DATA_cnt_1%4)*8))) << dec <<endl; 
#endif
			DATA_cnt_1++;
			if (DATA_cnt_1 == NAND_PAGE_SIZE)
			{
				current_state_1 = T_CONFIRM;
				DATA_cnt_1 = 0;
			}
			break;
		case T_RD_DATA:
#ifdef	DEBUG_SATA_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_1> T_RD_DATA"<<endl;
#endif
			DATA_word_1 = DATA_word_1 | NAND_port_1->NAND_read() << (DATA_cnt_1%4)*8;
			if ((DATA_cnt_1%4) == 3)
			{
				CH1_Buffer[DATA_cnt_1/4] = DATA_word_1;
#ifdef	DEBUG_SATA_SSD
				cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_1> CH1_Buffer["<< DATA_cnt_1/4 <<"] : 0x" << hex << DATA_word_1 << dec <<endl; 
#endif
				DATA_word_1 = 0;
			}
			DATA_cnt_1++;
			if (DATA_cnt_1 == NAND_PAGE_SIZE)
			{
				NAND_port_1->NAND_write(NAND_HI_Z);
				cache_buffer_read_1 = true;
				DATA_cnt_1 = 0;
				current_state_1 = T_IDLE;
			}
			break;
		case T_CONFIRM:
#ifdef	DEBUG_SATA_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_1> T_CONFIRM"<<endl;
#endif
			if (cmd_1 == NAND_START_PROGRAM)
			{
				if (!Confirm_commanded_1)
				{
					NAND_port_1->NAND_write(NAND_END_PROGRAM);
					Confirm_commanded_1 = true;
				}
				else if (NAND_port_1->NAND_read_R_B())
				{
					Confirm_commanded_1 = false;
					current_state_1 = T_READ_STATUS;
				}
			}
			else if (cmd_1 == NAND_START_READ)
			{
				if (!Confirm_commanded_1)
				{
					NAND_port_1->NAND_write(NAND_END_READ);
					Confirm_commanded_1 = true;
				}
				else if (NAND_port_1->NAND_read_R_B())
				{
					Confirm_commanded_1 = false;
					current_state_1 = T_RD_DATA;
				}
			}
			else if (cmd_1 == NAND_START_ERASE)
			{
				NAND_port_1->NAND_write(NAND_END_ERASE);
				current_state_1 = T_READ_STATUS;
			}
			break;
		case T_READ_STATUS:
#ifdef	DEBUG_SATA_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_1> T_READ_STATUS"<<endl;
#endif
			NAND_port_1->NAND_write(NAND_READ_STATUS);
			current_state_1 = IO_CHECK;
			break;
		case IO_CHECK:
#ifdef	DEBUG_SATA_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_1> IO_CHECK"<<endl;
#endif
			if (NAND_port_1->NAND_read() == NAND_IO_OK)
			{
#ifdef	DEBUG_SATA_SSD
				cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_1> IO_CHECK RESULT : OK" << endl;
#endif
				current_state_1 = T_IDLE;
				if (cmd_1 == NAND_START_PROGRAM)
				{
					if (!DRAM_or_SSD)
						SSD_Cache_finished = true;
					else
						DRAM_Cache_finished = true;
					SSD_DMA_PRD_fetched = false;
					SSD_DMA_buffered = false;
					SSD_write_end = 0x1;
					SSD_DMA_LBA_current += SSD_DMA_base_cnt;
//					PRD_current--;
					if (PRD_current == 0)
					{
						SSD_DMA_enable = false;
						SSD_DMA_end = 0x1;
//#ifdef	DEBUG_SATA_SSD
						cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> South SSD-DMA is finished" << endl;
//#endif
					}
				}
			}
			break;
		default:
			;
		}
		wait();
	}		
}

void
SATA_SSD::FLASHcontroller_process_2()
{
	while (true)
	{
		switch(current_state_2)
		{
		case T_IDLE:
			if (F_WRITE_2 || F_READ_2 || F_ERASE_2)
			{
#ifdef	DEBUG_SATA_SSD
				cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_2> T_IDLE"<<endl;
#endif
				current_state_2 = T_COMMAND;
			}
			break;
		case T_COMMAND:
#ifdef	DEBUG_SATA_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_2> T_COMMAND"<<endl;
#endif
			if (F_WRITE_2)
			{ 
				FLASH_addr_gen(LBA_write);
				cmd_2 = NAND_START_PROGRAM;
				F_WRITE_2 = false;
				current_state_2 = T_COL_ADDR0;
			}
			else if (F_READ_2)
			{
				FLASH_addr_gen(LBA_read);
				cmd_2 = NAND_START_READ;
				F_READ_2 = false;
				current_state_2 = T_COL_ADDR0;
			}
			else if (F_ERASE_2)
			{
				cmd_2 = NAND_START_ERASE;
				F_ERASE_2 = false;
				current_state_2 = T_ROW_ADDR0;
			}
			else
			{
				cout << "[" << sc_time_stamp() << "] FLASH_CTRL_2> Unsupported command!" << endl;
				sc_stop();
			}
			NAND_port_2->NAND_write(cmd_2);
			break;
		case T_COL_ADDR0:
#ifdef	DEBUG_SATA_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_2> T_COL_ADDR0"<<endl;
#endif
			current_state_2 = T_COL_ADDR1;
			NAND_port_2->NAND_write(col_addr0);
			break;
		case T_COL_ADDR1:
#ifdef	DEBUG_SATA_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_2> T_COL_ADDR1"<<endl;
#endif
			current_state_2 = T_ROW_ADDR0;
			NAND_port_2->NAND_write(col_addr1);
			break;
		case T_ROW_ADDR0:
#ifdef	DEBUG_SATA_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_2> T_ROW_ADDR0"<<endl;
#endif
			current_state_2 = T_ROW_ADDR1;
			NAND_port_2->NAND_write(row_addr0);
			break;
		case T_ROW_ADDR1:
#ifdef	DEBUG_SATA_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_2> T_ROW_ADDR1"<<endl;
#endif
			NAND_port_2->NAND_write(row_addr1);
			if (cmd_2 == NAND_START_ERASE)
				current_state_2 = T_WR_DATA;
			else if (cmd_2 == NAND_START_READ)
				current_state_2 = T_CONFIRM;
			else if (cmd_2 == NAND_START_ERASE)
				current_state_2 = T_CONFIRM;
			break;
		case T_WR_DATA:
#ifdef	DEBUG_SATA_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_2> T_WR_DATA"<<endl;
#endif
			NAND_port_2->NAND_write((byte)(CH1_Buffer[DATA_cnt_2/4] >> ((DATA_cnt_2%4)*8)));
			DATA_cnt_2++;
			if (DATA_cnt_2 == NAND_PAGE_SIZE)
			{
				current_state_2 = T_CONFIRM; 
				DATA_cnt_2 = 0;
			}
			break;
		case T_RD_DATA:
#ifdef	DEBUG_SATA_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_2> T_RD_DATA"<<endl;
#endif
			DATA_word_2 = DATA_word_2 | NAND_port_2->NAND_read() << (DATA_cnt_2%4)*8;
			if ((DATA_cnt_2%4) == 3)
			{
				CH1_Buffer[DATA_cnt_2/4] = DATA_word_2;
#ifdef	DEBUG_SATA_SSD
				cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_2> CH1_Buffer["<< DATA_cnt_2/4 <<"] : 0x" << hex << DATA_word_2 << dec << endl; 
#endif
				DATA_word_2 = 0;
			}
			DATA_cnt_2++;
			if (DATA_cnt_2 == NAND_PAGE_SIZE)
			{
				cache_buffer_read_2 = true;
				DATA_cnt_2 = 0;
				current_state_2 = T_IDLE;
			}
			break;
		case T_CONFIRM:
#ifdef	DEBUG_SATA_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_2> T_CONFIRM"<<endl;
#endif
			if (cmd_2 == NAND_START_PROGRAM)
			{
				if (!Confirm_commanded_2)
				{
					NAND_port_2->NAND_write(NAND_END_PROGRAM);
					Confirm_commanded_2 = true;
				}
				else if (NAND_port_2->NAND_read_R_B())
				{
					Confirm_commanded_2 = false;
					current_state_2 = T_READ_STATUS;
				}
			}
			else if (cmd_2 == NAND_START_READ)
			{
				if (!Confirm_commanded_2)
				{
					NAND_port_2->NAND_write(NAND_END_READ);
					Confirm_commanded_2 = true;
				}
				else if (NAND_port_2->NAND_read_R_B())
				{
					Confirm_commanded_2 = false;
					current_state_2 = T_RD_DATA;
				}
			}
			else if (cmd_2 == NAND_START_ERASE)
			{
				NAND_port_2->NAND_write(NAND_END_ERASE);
				current_state_2 = T_READ_STATUS;
			}
			break;
		case T_READ_STATUS:
#ifdef	DEBUG_SATA_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_2> T_READ_STATUS"<<endl;
#endif
			NAND_port_2->NAND_write(NAND_READ_STATUS);
			current_state_2 = IO_CHECK;
			break;
		case IO_CHECK:
#ifdef	DEBUG_SATA_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_2> IO_CHECK"<<endl;
#endif
			if (NAND_port_2->NAND_read() == NAND_IO_OK)
			{
#ifdef	DEBUG_SATA_SSD
				cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_2> IO_CHECK RESULT : OK" << endl;
#endif
				current_state_2 = T_IDLE;
				if (cmd_2 == NAND_START_PROGRAM)
				{
					if (!DRAM_or_SSD)
						SSD_Cache_finished = true;
					else
						DRAM_Cache_finished = true;
					SSD_DMA_PRD_fetched = false;
					SSD_DMA_buffered = false;
					SSD_write_end = 0x1;
					SSD_DMA_LBA_current += SSD_DMA_base_cnt;
//					PRD_current--;
					if (PRD_current == 0)
					{
						SSD_DMA_enable = false;
						SSD_DMA_end = 0x1;
//#ifdef	DEBUG_SATA_SSD
						cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> South SSD-DMA is finished" << endl;
//#endif
					}
				}
			}
			break;
		default:
			;
		}
		wait();
	}		
}

void
SATA_SSD::FLASH_addr_gen(unsigned	LBA_)
{
	PA = LBA_;
	BA = LBA_ >> 6;
	
	col_addr0 = 0;
	col_addr1 = 0;
	row_addr0 = ((BA << 6) & 0x3) | PA;
	row_addr1 = BA >> 2 & 0xFF;
}

void
SATA_SSD::DRAM_timing_control_action()
{
	const unsigned DRAM_tRCD = DDR_DEFAULT_tRCD;
	const unsigned DRAM_CL = DDR_DEFAULT_CL;
	const unsigned DRAM_BL = DDR_DEFAULT_BURST_LENGTH;
	const unsigned DRAM_tDQSS = DDR_DEFAULT_tDQSS;
	const unsigned DRAM_tWR = DDR_DEFAULT_tWR;
	const unsigned DRAM_tRP = DDR_DEFAULT_tRP;
	const unsigned DRAM_tRFC = DDR_DEFAULT_tRFC;

	unsigned tRFC_cnt = DRAM_tRFC;

	unsigned DRAM_rank_num = pow(2, DRAM_rank_index);
	unsigned DRAM_bank_num = pow(2, DRAM_bank_index);

	SDRAM_bank_interleave_state_t DRAM_curr_state = IDLE_STATE;

	unsigned full_data_length;
	SDRAM_DDR_data_t SDRAM_current_data;
	unsigned full_banks_num;
	unsigned remained_banks_num;
	unsigned row_activate_cnt;
	unsigned start_bank;
	unsigned end_bank;

	SDRAM_DDR_bank_control_info_t* bank_manager;

	bool command_sent;
	bool data_reserved;

	SDRAM_addr_t addr;

	while (true)
	{
		int bank_cnt;

		switch(DRAM_curr_state)
		{
		case IDLE_STATE:
			if ((DRAM_refresh_enable) && (DRAM_CLK == true))
			{
#ifdef	DEBUG_SATA_SSD_SDRAM_CTRL
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Start Auto Refresh." << endl;
#endif
				DRAM_port->SDRAM_refresh(1, false);
				DRAM_curr_state = REFRESH_STATE;
#ifdef	DEBUG_SATA_SSD_SDRAM_CTRL
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Now Refreshing..." << tRFC_cnt << endl;
#endif
				tRFC_cnt--;
			}
			else if ((SSD_DMA_enable) && (SSD_DMA_enable_time != sc_time_stamp()) &&
				((!SSD_DMA_buffered && SSD_DMA_command == 0xCA) || (SSD_DMA_buffered && SSD_DMA_command == 0xC8)))
			{
				if ((!SSD_DMA_PRD_fetched && !SSD_DMA_buffered && SSD_DMA_command == 0xCA) || (!SSD_DMA_PRD_fetched && SSD_DMA_buffered && SSD_DMA_command == 0xC8))
				{
					addr = SSD_DMA_PRD_start_addr;
					if ((2*SSD_DMA_PRD_length)%(DRAM_BL*((DDR_EXT_BITWIDTH_MODULE/8)/4)) == 0)
						full_data_length = 2*SSD_DMA_PRD_length;
					else
						full_data_length = (2*SSD_DMA_PRD_length) + ((DRAM_BL*((DDR_EXT_BITWIDTH_MODULE/8)/4))-(2*SSD_DMA_PRD_length)%(DRAM_BL*((DDR_EXT_BITWIDTH_MODULE/8)/4)));
//#ifdef	DEBUG_SATA_SSD_SDRAM_CTRL
					cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "PRD for South SSD-DMA is requested." << endl;
//#endif
				}
				else
				{
//#ifdef	DEBUG_SATA_SSD_SDRAM_CTRL
					if (SSD_DMA_command == 0xC8)
						cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Send data to main memory"<< endl;
					else
						cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Request data to main memory"<< endl;
//#endif
					addr = SSD_DMA_base_addr;
					full_data_length = 1024;
				}
				full_banks_num = remained_banks_num = full_data_length / ((DRAM_BL * DDR_EXT_BITWIDTH_MODULE/8) /4);
				row_activate_cnt = 0;
				start_bank = (addr >> 5) & 0x7;
				end_bank = ((((addr + full_data_length*4) >> 5) & 0x7) - 1) % (DRAM_rank_num*DRAM_bank_num);
				bank_manager = new SDRAM_DDR_bank_control_info_t[DRAM_rank_num*DRAM_bank_num];
				for (int i=0;i<DRAM_rank_num*DRAM_bank_num;i++)
				{
					bank_manager[i].bank_curr_state = SDRAM_IDLE;
					bank_manager[i].tRCD_cnt = DRAM_tRCD;
					bank_manager[i].CL_cnt = DRAM_CL;
					bank_manager[i].read_data_enable = false;
					bank_manager[i].BL_cnt = DRAM_BL;
					bank_manager[i].read_auto_precharge_cnt = DRAM_BL;
					bank_manager[i].tDQSS_cnt = DRAM_tDQSS;
					bank_manager[i].tWR_cnt = DRAM_tWR;
					bank_manager[i].tRP_cnt = DRAM_tRP;
					if ((full_banks_num%(DRAM_rank_num*DRAM_bank_num) == 0) && (start_bank == 0))
					{
						bank_manager[i].bank_addr = new SDRAM_addr_t[full_banks_num/(DRAM_rank_num*DRAM_bank_num)];
						bank_manager[i].bank_last = full_banks_num/(DRAM_rank_num*DRAM_bank_num)-1;
					}
					else if (start_bank <= end_bank)
					{
						bank_manager[i].bank_addr = new SDRAM_addr_t[full_banks_num/(DRAM_rank_num*DRAM_bank_num)+1];
						bank_manager[i].bank_last = full_banks_num/(DRAM_rank_num*DRAM_bank_num);
					}
					else
					{
						if (end_bank+1 == start_bank)
						{
							bank_manager[i].bank_addr = new SDRAM_addr_t[full_banks_num/(DRAM_rank_num*DRAM_bank_num)+1];
							bank_manager[i].bank_last = full_banks_num/(DRAM_rank_num*DRAM_bank_num);
						}
						else
						{
							bank_manager[i].bank_addr = new SDRAM_addr_t[full_banks_num/(DRAM_rank_num*DRAM_bank_num)+2];
							bank_manager[i].bank_last = full_banks_num/(DRAM_rank_num*DRAM_bank_num)+1;
						}
					}
					bank_manager[i].bank_first = 0;
				}
				unsigned addr_line = 0;
				for (int i=0;i<full_banks_num;i++)
				{
					if ((i != 0) && ((start_bank+i)%(DRAM_rank_num*DRAM_bank_num) == 0))
						addr_line++;
					bank_manager[(start_bank+i)%(DRAM_rank_num*DRAM_bank_num)].bank_addr[addr_line] = addr + 0x20 * i;
				}
				for (int i=0;i<start_bank;i++)
					bank_manager[i].bank_first++;
				if (end_bank < DRAM_rank_num*DRAM_bank_num-1)
				{
					for (int i=end_bank+1;i<DRAM_rank_num*DRAM_bank_num;i++)
					{
						if (bank_manager[i].bank_last !=0)
							bank_manager[i].bank_last--;
						else
							bank_manager[i].bank_first++;
					}
				}
				if ((!SSD_DMA_PRD_fetched) || (!SSD_DMA_buffered && SSD_DMA_command == 0xCA))
				{
					DRAM_curr_state = READ_STATE;
#ifdef	DEBUG_SATA_SSD_SDRAM_CTRL
					cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Start Read to DRAM port" << endl;
#endif
				}
				else
				{
					DRAM_curr_state = WRITE_STATE;
#ifdef	DEBUG_SATA_SSD_SDRAM_CTRL
					cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Start Write to DRAM port" << endl;
#endif
				}
				data_reserved = false;
			}
			else
			{
/*
#ifdef	DEBUG_SATA_SSD_SDRAM_CTRL
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> MRS commanded : 0xA62 (burst_length = 4)" << endl;
#endif
				DRAM_port->SDRAM_DDR_mode_register_set(1, 0, 0xA62);
				DRAM_curr_state = EMRS_STATE;
*/
			}
			break;
		case READ_STATE:
			command_sent = false;
			bank_cnt = 0;
			for (int i=start_bank;bank_cnt<8;i=(i+1)%(DRAM_rank_num*DRAM_bank_num))
			{
				if (bank_manager[i].bank_first<=bank_manager[i].bank_last)
				{
					unsigned rank_sel = (bank_manager[i].bank_addr[bank_manager[i].bank_first] >> 5) & 0x1;
					unsigned bank_sel = (bank_manager[i].bank_addr[bank_manager[i].bank_first] >> 6) & 0x3;
					SDRAM_addr_t row_addr = (bank_manager[i].bank_addr[bank_manager[i].bank_first] >> 16) & 0x1FFF;
					SDRAM_addr_t col_addr = ((bank_manager[i].bank_addr[bank_manager[i].bank_first] >> 6) & 0x3FC) | ((bank_manager[i].bank_addr[bank_manager[i].bank_first] >> 3) & 0x3);
					switch (bank_manager[i].bank_curr_state)
					{
					case SDRAM_IDLE:
						if ((DRAM_CLK == true) && (!command_sent) && (DRAM_port->SDRAM_DDR_get_DQS(1, rank_sel, bank_sel) == 'Z'))
						{
#ifdef	DEBUG_SATA_SSD_SDRAM_CTRL
							cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Row Activate at Rank=" << hex << rank_sel << ", Bank=" << bank_sel << ", Row=0x" << row_addr << dec << endl;
#endif
							DRAM_port->SDRAM_row_activate(1, rank_sel, bank_sel, row_addr);
							command_sent = true;
							row_activate_cnt++;
#ifdef	DEBUG_SATA_SSD_SDRAM_CTRL
							cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Waiting Row Activate...tRCD count(" << rank_sel << ", " << bank_sel << ") : " << bank_manager[i].tRCD_cnt << endl;
#endif
							bank_manager[i].bank_curr_state = SDRAM_ROW_ACT;
							bank_manager[i].tRCD_cnt--;
						}
						break;
					case SDRAM_ROW_ACT:
						if (DRAM_CLK == true)
						{
							if (DRAM_port->SDRAM_DDR_get_DQS(1, rank_sel, bank_sel) != 'Z')
							{
								bank_manager[i].bank_curr_state = SDRAM_IDLE;
								row_activate_cnt--;
								bank_manager[i].tRCD_cnt++;
							}
							else
							{
#ifdef	DEBUG_SATA_SSD_SDRAM_CTRL
								cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Waiting Row Activate...tRCD count(" << rank_sel << ", " << bank_sel << ") : " << bank_manager[i].tRCD_cnt << endl;
#endif
								bank_manager[i].tRCD_cnt--;
								if (bank_manager[i].tRCD_cnt == 0)
								{
									bank_manager[i].bank_curr_state = SDRAM_READ_AP;
									bank_manager[i].tRCD_cnt = DRAM_tRCD;
								}
							}
						}
						break;
					case SDRAM_READ_AP:
						if (bank_manager[i].CL_cnt == DRAM_CL)
						{
							if ((DRAM_CLK == true) && (!command_sent) && (!data_reserved))
							{
								if (((remained_banks_num < DRAM_rank_num*DRAM_bank_num) && (row_activate_cnt >= remained_banks_num)) ||
									((remained_banks_num >= DRAM_rank_num*DRAM_bank_num) && (row_activate_cnt == DRAM_rank_num*DRAM_bank_num)))
								{
#ifdef	DEBUG_SATA_SSD_SDRAM_CTRL
									cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Read Command at Rank=" << hex << rank_sel << ", Bank=" << bank_sel << ", Col=0x" << col_addr << dec << endl;
#endif
									DRAM_port->SDRAM_read_command(1, rank_sel, bank_sel, col_addr, true);
									command_sent = true;
									data_reserved = true;
#ifdef	DEBUG_SATA_SSD_SDRAM_CTRL
									cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Waiting CAS Latency...CL count(" << rank_sel << ", " << bank_sel << ") : " << bank_manager[i].CL_cnt << endl;
#endif
									bank_manager[i].CL_cnt--;
									bank_manager[i].read_auto_precharge_cnt--;
								}
							}
						}
						else if (bank_manager[i].CL_cnt != 0)
						{
							if (DRAM_CLK == true)
							{
#ifdef	DEBUG_SATA_SSD_SDRAM_CTRL
								cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Waiting CAS Latency...CL count(" << rank_sel << ", " << bank_sel << ") : " << bank_manager[i].CL_cnt << endl;
#endif
								bank_manager[i].CL_cnt--;
							}
							if (bank_manager[i].read_auto_precharge_cnt != 0)
							{
								bank_manager[i].read_auto_precharge_cnt--;
								if (bank_manager[i].read_auto_precharge_cnt == 0)
									data_reserved = false;
							}
							else if ((DRAM_CLK == true) && (bank_manager[i].tRP_cnt != 0))
							{
#ifdef	DEBUG_SATA_SSD_SDRAM_CTRL
								cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Waiting Precharge...tRP count(" << rank_sel << ", " << bank_sel << ") : " << bank_manager[i].tRP_cnt << endl;
#endif
								bank_manager[i].tRP_cnt--;
							}
						}
						else
						{
							if (bank_manager[i].read_auto_precharge_cnt != 0)
							{
								bank_manager[i].read_auto_precharge_cnt--;
								if (bank_manager[i].read_auto_precharge_cnt == 0)
									data_reserved = false;
							}
							else if ((DRAM_CLK == true) && (bank_manager[i].tRP_cnt != 0))
							{
#ifdef	DEBUG_SATA_SSD_SDRAM_CTRL
								cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Waiting Precharge...tRP count(" << rank_sel << ", " << bank_sel << ") : " << bank_manager[i].tRP_cnt << endl;
#endif
								bank_manager[i].tRP_cnt--;
							}
							if (bank_manager[i].read_data_enable)
							{
								if (DRAM_port->SDRAM_DDR_get_DQS(1, rank_sel, bank_sel) != 'Z')
								{
#ifdef	DEBUG_SATA_SSD_SDRAM_CTRL
									cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Before Read data : 0x" << hex << SDRAM_current_data.upper_bytes << " | 0x" << SDRAM_current_data.lower_bytes << dec;
#endif
									SDRAM_current_data = DRAM_port->SDRAM_DDR_read_data(1, rank_sel, bank_sel, DDR_DEFAULT_BURST_LENGTH-bank_manager[i].BL_cnt, false);
									DRAM_WR_FIFO[(bank_manager[i].bank_addr[bank_manager[i].bank_first]-addr)/4+2*(DRAM_BL-bank_manager[i].BL_cnt)] = SDRAM_current_data.lower_bytes;
									DRAM_WR_FIFO[(bank_manager[i].bank_addr[bank_manager[i].bank_first]-addr)/4+2*(DRAM_BL-bank_manager[i].BL_cnt)+1] = SDRAM_current_data.upper_bytes;
#ifdef	DEBUG_SATA_SSD_SDRAM_CTRL
									cout << ", After Read data : 0x" << hex << SDRAM_current_data.upper_bytes << " | 0x" << SDRAM_current_data.lower_bytes << dec << endl;
#endif
									bank_manager[i].BL_cnt--;
									if (bank_manager[i].BL_cnt == 0)
									{
										bank_manager[i].CL_cnt = DRAM_CL;
										bank_manager[i].read_data_enable = false;
										bank_manager[i].BL_cnt = DRAM_BL;
										bank_manager[i].read_auto_precharge_cnt = DRAM_BL;
										if (bank_manager[i].tRP_cnt != 0)
										{
											bank_manager[i].bank_curr_state = SDRAM_PRECHARGE;
#ifdef	DEBUG_SATA_SSD_SDRAM_CTRL
											cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Waiting Precharge...tRP count(" << rank_sel << ", " << bank_sel << ") : " << bank_manager[i].tRP_cnt << endl;
#endif
											bank_manager[i].tRP_cnt--;
										}
										else
										{
											if (i == start_bank)
												start_bank = (start_bank+1)%(DRAM_rank_num*DRAM_bank_num);
											bank_manager[i].bank_first++;
											bank_manager[i].bank_curr_state=SDRAM_IDLE;
											bank_manager[i].tRP_cnt = DRAM_tRP;
											remained_banks_num--;
											if (bank_manager[i].bank_first<=bank_manager[i].bank_last)
												row_activate_cnt--;
											if (remained_banks_num == 0)
											{
												unsigned data_finished = 0;
												for (int j=0;j<DRAM_rank_num*DRAM_bank_num;j++)
													if ((bank_manager[j].bank_curr_state == SDRAM_IDLE) && (bank_manager[j].bank_first>bank_manager[j].bank_last))
														data_finished++;
												if (data_finished == DRAM_rank_num*DRAM_bank_num)
												{
													if (!SSD_DMA_PRD_fetched)
													{
														SSD_DMA_base_addr = DRAM_WR_FIFO[0];
														SSD_DMA_base_cnt = DRAM_WR_FIFO[1] & 0xFFFF;
														SSD_DMA_PRD_start_addr = SSD_DMA_PRD_start_addr + 2*4;
														SSD_DMA_PRD_fetched = true;
														SSD_DMA_PRD_fetched_time = sc_time_stamp();
														SSD_DMA_LBA_current += SSD_DMA_base_cnt;
														PRD_current--;
														if (PRD_current != 0 && SSD_DMA_command == 0xC8)
															DRAM_Cache_RD_request = true;
//#ifdef	DEBUG_SATA_SSD_SDRAM_CTRL
														cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> PRD for South SSD-DMA is fetched" << endl;
//#endif
													}
													else if (!SSD_DMA_buffered)
													{
														LBA_write = SSD_DMA_LBA_current;
//#ifdef	DEBUG_SATA_SSD_SDRAM_CTRL
														cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Write to FIFO is finished...Enable Write to CACHE." <<endl;
//#endif
														SSD_DMA_buffered = true;
														SSD_DMA_buffered_time = sc_time_stamp();
														DRAM_Cache_WR_request = true;
													}
													else
													{
														cout << "ERROR @ " << sc_module::name() << "[" << sc_time_stamp() << "]> These data are not requested." << endl;
														sc_stop();
													}
													DRAM_curr_state = IDLE_STATE;
												}
											}
										}
									}
								}
							}
							else
							{
								bank_manager[i].read_data_enable = true;
							}
						}
						break;
					case SDRAM_PRECHARGE:
						if (DRAM_CLK == true)
						{
#ifdef	DEBUG_SATA_SSD_SDRAM_CTRL
							cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Waiting Precharge...tRP count(" << rank_sel << ", " << bank_sel << ") : " << bank_manager[i].tRP_cnt << endl;
#endif
							if (bank_manager[i].tRP_cnt != 0)
								bank_manager[i].tRP_cnt--;
							if(bank_manager[i].tRP_cnt == 0)
							{
								if (i == start_bank)
									start_bank = (start_bank+1)%(DRAM_rank_num*DRAM_bank_num);
								bank_manager[i].bank_first++;
								bank_manager[i].bank_curr_state=SDRAM_IDLE;
								bank_manager[i].tRP_cnt = DRAM_tRP;
								remained_banks_num--;
								if (bank_manager[i].bank_first<=bank_manager[i].bank_last)
									row_activate_cnt--;
								if (remained_banks_num == 0)
								{
									unsigned data_finished = 0;
									for (int j=0;j<DRAM_rank_num*DRAM_bank_num;j++)
										if ((bank_manager[j].bank_curr_state == SDRAM_IDLE) && (bank_manager[j].bank_first>bank_manager[j].bank_last))
											data_finished++;
									if (data_finished == DRAM_rank_num*DRAM_bank_num)
									{
										if (!SSD_DMA_PRD_fetched)
										{
											SSD_DMA_base_addr = DRAM_WR_FIFO[0];
											SSD_DMA_base_cnt = DRAM_WR_FIFO[1] & 0xFFFF;
											SSD_DMA_PRD_start_addr = SSD_DMA_PRD_start_addr + 2*4;
											SSD_DMA_PRD_fetched = true;
											SSD_DMA_PRD_fetched_time = sc_time_stamp();
											SSD_DMA_LBA_current += SSD_DMA_base_cnt;
											PRD_current--;
											if (PRD_current != 0 && SSD_DMA_command == 0xC8)
												DRAM_Cache_RD_request = true;
//#ifdef	DEBUG_SATA_SSD_SDRAM_CTRL
											cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> PRD for South SSD-DMA is fetched" << endl;
//#endif
										}
										else if (!SSD_DMA_buffered)
										{
											LBA_write = SSD_DMA_LBA_current;
//#ifdef	DEBUG_SATA_SSD_SDRAM_CTRL
											cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Write to FIFO is finished...Enable Write to CACHE." <<endl;
//#endif
											SSD_DMA_buffered = true;
											SSD_DMA_buffered_time = sc_time_stamp();
											DRAM_Cache_WR_request = true;
										}
										else
										{
											cout << "ERROR @ " << sc_module::name() << "[" << sc_time_stamp() << "]> These data are not requested." << endl;
											sc_stop();
										}
										DRAM_curr_state = IDLE_STATE;
									}
								}
							}
						}
						break;
					default:
						cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "Unknown State!" << endl << flush;
						sc_stop();
					}
				}
				bank_cnt++;
			}
			break;
		case WRITE_STATE:
			command_sent = false;
			bank_cnt = 0;
			for (int i=start_bank;bank_cnt<8;i=(i+1)%(DRAM_rank_num*DRAM_bank_num))
			{
				if (bank_manager[i].bank_first<=bank_manager[i].bank_last)
				{
					unsigned rank_sel = (bank_manager[i].bank_addr[bank_manager[i].bank_first] >> 5) & 0x1;
					unsigned bank_sel = (bank_manager[i].bank_addr[bank_manager[i].bank_first] >> 6) & 0x3;
					SDRAM_addr_t row_addr = (bank_manager[i].bank_addr[bank_manager[i].bank_first] >> 16) & 0x1FFF;
					SDRAM_addr_t col_addr = ((bank_manager[i].bank_addr[bank_manager[i].bank_first] >> 6) & 0x3FC) | ((bank_manager[i].bank_addr[bank_manager[i].bank_first] >> 3) & 0x3);
					switch (bank_manager[i].bank_curr_state)
					{
					case SDRAM_IDLE:
						if ((DRAM_CLK == true) && (!command_sent) && (DRAM_port->SDRAM_DDR_get_DQS(1, rank_sel, bank_sel) == 'Z'))
						{
#ifdef	DEBUG_SATA_SSD_SDRAM_CTRL
							cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Row Activate at Rank=" << hex << rank_sel << ", Bank=" << bank_sel << ", Row=0x" << row_addr << dec << endl;
#endif
							DRAM_port->SDRAM_row_activate(1, rank_sel, bank_sel, row_addr);
							command_sent = true;
							row_activate_cnt++;
#ifdef	DEBUG_SATA_SSD_SDRAM_CTRL
							cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Waiting Row Activate...tRCD count(" << rank_sel << ", " << bank_sel << ") : " << bank_manager[i].tRCD_cnt << endl;
#endif
							bank_manager[i].bank_curr_state = SDRAM_ROW_ACT;
							bank_manager[i].tRCD_cnt--;
						}
						break;
					case SDRAM_ROW_ACT:
						if (DRAM_CLK == true)
						{
							if (DRAM_port->SDRAM_DDR_get_DQS(1, rank_sel, bank_sel) != 'Z')
							{
								bank_manager[i].bank_curr_state = SDRAM_IDLE;
								row_activate_cnt--;
								bank_manager[i].tRCD_cnt++;
							}
							else
							{
#ifdef	DEBUG_SATA_SSD_SDRAM_CTRL
								cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Waiting Row Activate...tRCD count(" << rank_sel << ", " << bank_sel << ") : " << bank_manager[i].tRCD_cnt << endl;
#endif
								bank_manager[i].tRCD_cnt--;
								if (bank_manager[i].tRCD_cnt == 0)
								{
									bank_manager[i].bank_curr_state = SDRAM_WRITE_AP;
									bank_manager[i].tRCD_cnt = DRAM_tRCD;
								}
							}
						}
						break;
					case SDRAM_WRITE_AP:
						if (bank_manager[i].tDQSS_cnt == DRAM_tDQSS)
						{
							if ((DRAM_CLK == true) && (!command_sent) && (!data_reserved))
							{
								if (((remained_banks_num < DRAM_rank_num*DRAM_bank_num) && (row_activate_cnt >= remained_banks_num)) ||
									((remained_banks_num >= DRAM_rank_num*DRAM_bank_num) && (row_activate_cnt == DRAM_rank_num*DRAM_bank_num)))
								{
#ifdef	DEBUG_SATA_SSD_SDRAM_CTRL
									cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Write Command at Rank=" << hex << rank_sel << ", Bank=" << bank_sel << ", Col=0x" << col_addr << dec << endl;
#endif
									DRAM_port->SDRAM_write_command(1, rank_sel, bank_sel, col_addr, true);
									if (DRAM_port->SDRAM_DDR_get_DQS(1, rank_sel, bank_sel) == 'Z')
									{
										DRAM_port->SDRAM_DDR_set_DQS(1, rank_sel, bank_sel, sc_logic('0'));
									}
									else
									{
										cout << "ERROR @ " << sc_module::name() << "[" << sc_time_stamp() << "]> DQS signal is not valid." << endl;
										sc_stop();
									}
									command_sent = true;
									data_reserved = true;
#ifdef	DEBUG_SATA_SSD_SDRAM_CTRL
									cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Waiting DQS Transition...tDQSS count(" << rank_sel << ", " << bank_sel << ") : " << bank_manager[i].tDQSS_cnt << endl;
#endif
									bank_manager[i].tDQSS_cnt--;
								}
							}
						}
						else
						{
							if (bank_manager[i].tDQSS_cnt != 0)
							{
#ifdef	DEBUG_SATA_SSD_SDRAM_CTRL
								cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Waiting DQS Transition...tDQSS count(" << rank_sel << ", " << bank_sel << ") : " << bank_manager[i].tDQSS_cnt << endl;
#endif
								bank_manager[i].tDQSS_cnt--;
							}
							else
							{
								if (bank_manager[i].BL_cnt != 0)
								{
									SDRAM_current_data.lower_bytes = DRAM_RD_FIFO[(bank_manager[i].bank_addr[bank_manager[i].bank_first]-addr)/4+2*(DRAM_BL-bank_manager[i].BL_cnt)];
									SDRAM_current_data.upper_bytes = DRAM_RD_FIFO[(bank_manager[i].bank_addr[bank_manager[i].bank_first]-addr)/4+2*(DRAM_BL-bank_manager[i].BL_cnt)+1];
#ifdef	DEBUG_SATA_SSD_SDRAM_CTRL
									cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Write data : 0x" << hex << SDRAM_current_data.upper_bytes << " | 0x" << SDRAM_current_data.lower_bytes << dec << endl;
#endif
									DRAM_port->SDRAM_DDR_write_data(1, rank_sel, bank_sel, DDR_DEFAULT_BURST_LENGTH-bank_manager[i].BL_cnt, SDRAM_current_data, false);
									if (DRAM_port->SDRAM_DDR_get_DQS(1, rank_sel, bank_sel) == '0')
									{
										DRAM_port->SDRAM_DDR_set_DQS(1, rank_sel, bank_sel, sc_logic('1'));
									}
									else if (DRAM_port->SDRAM_DDR_get_DQS(1, rank_sel, bank_sel) == '1')
									{
										DRAM_port->SDRAM_DDR_set_DQS(1, rank_sel, bank_sel, sc_logic('0'));
									}
									else
									{
										cout << "ERROR @ " << sc_module::name() << "[" << sc_time_stamp() << "]> DQS signal is not valid." << endl;
										sc_stop();
									}
									bank_manager[i].BL_cnt--;
									if (bank_manager[i].BL_cnt == 0)
									{
										DRAM_port->SDRAM_DDR_set_DQS(1, rank_sel, bank_sel, sc_logic('Z'));
										data_reserved = false;
									}
								}
								if ((bank_manager[i].BL_cnt == 0) && (DRAM_CLK == true))
								{
									if (bank_manager[i].tWR_cnt != 0)
									{
#ifdef	DEBUG_SATA_SSD_SDRAM_CTRL
										cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Waiting Precharge...tWR count(" << rank_sel << ", " << bank_sel << ") : " << bank_manager[i].tWR_cnt << endl;
#endif
										bank_manager[i].tWR_cnt--;
									}
									else
									{
										bank_manager[i].bank_curr_state= SDRAM_PRECHARGE;
										bank_manager[i].BL_cnt = DRAM_BL;
										bank_manager[i].tDQSS_cnt = DRAM_tDQSS;
										bank_manager[i].tWR_cnt = DRAM_tWR;
#ifdef	DEBUG_SATA_SSD_SDRAM_CTRL
										cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Waiting Precharge...tRP count(" << rank_sel << ", " << bank_sel << ") : " << bank_manager[i].tRP_cnt << endl;
#endif
										bank_manager[i].tRP_cnt--;
									}
								}
							}
						}
						break;
					case SDRAM_PRECHARGE:
						if (DRAM_CLK == true)
						{
#ifdef	DEBUG_SATA_SSD_SDRAM_CTRL
							cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Waiting Precharge...tRP count(" << rank_sel << ", " << bank_sel << ") : " << bank_manager[i].tRP_cnt << endl;
#endif
							bank_manager[i].tRP_cnt--;
							if(bank_manager[i].tRP_cnt==0)
							{
								if (i == start_bank)
									start_bank = (start_bank+1)%(DRAM_rank_num*DRAM_bank_num);
								bank_manager[i].bank_first++;
								bank_manager[i].bank_curr_state=SDRAM_IDLE;
								bank_manager[i].tRP_cnt = DRAM_tRP;
								remained_banks_num--;
								if (bank_manager[i].bank_first<=bank_manager[i].bank_last)
									row_activate_cnt--;
								if (remained_banks_num == 0)
								{
									unsigned data_finished = 0;
									for (int j=0;j<DRAM_rank_num*DRAM_bank_num;j++)
										if ((bank_manager[j].bank_curr_state == SDRAM_IDLE) && (bank_manager[j].bank_first>bank_manager[j].bank_last))
											data_finished++;
									if (data_finished == DRAM_rank_num*DRAM_bank_num)
									{
										SSD_DMA_PRD_fetched = false;
										SSD_DMA_buffered = false;
//#ifdef	DEBUG_SATA_SSD_SDRAM_CTRL
										cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "Write to Memory complete." << endl;
//#endif
										if (PRD_current == 0)
										{
											SSD_DMA_enable = false;
											SSD_DMA_end = 0x1;
//#ifdef	DEBUG_SATA_SSD_SDRAM_CTRL
											cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> South SSD-DMA is finished" << endl;
//#endif
										}
										DRAM_curr_state = IDLE_STATE;
									}
								}
							}
						}
						break;
					default:
						cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "Unknown State!" << endl << flush;
						sc_stop();
					}
				}
				bank_cnt++;
			}
			break;
		case EMRS_STATE:
			if (DRAM_CLK == true)
				DRAM_curr_state = IDLE_STATE;
			break;
		case REFRESH_STATE:
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "Unsupported State for this port!" << endl << flush;
			sc_stop();
			break;
		default:
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "Unknown State!" << endl << flush;
			sc_stop();
		}
		wait();
	}
}